package BLC

import (
	"encoding/hex"
	"fmt"
	"github.com/boltdb/bolt"
	"log"
)

//数据库的名字
const dbFile="blockchain.db"

//相当于表的名字
const blockBucket="blocks"

type BlockChain struct{
	Tip []byte  //区块链里面最后一个区块的hash值
	DB *bolt.DB
}

//查找未花费的交易列表
//返回交易数组(全部都是关于某个账号的)
func (bc *BlockChain)FindUnspentTransactions(address string)[]*Transaction{
	//存储未花费输出的交易
	var unspentTXs []*Transaction

	var block *Block

	var spentTXOs=make(map[string][]int)

	iter:=bc.NewIterator()

	for {
		block,iter=iter.Next()
		if iter==nil{
			break
		}
		for _,transaction:=range block.Transactions{
			TxID:=hex.EncodeToString(transaction.ID)
			Outputs:
				for outIdx,out:=range transaction.Vout{
					if spentTXOs[TxID]!=nil{
						for _,spentOut:=range spentTXOs[TxID]{
							//如果索引已经被包含在花费里面了，就可以继续下一个输出索引的遍历
							if outIdx==spentOut{
								continue Outputs
							}
						}
					}

					if out.CanBeUnlockedWith(address){
						unspentTXs=append(unspentTXs,transaction)
					}
				}
				if transaction.IsCoinBase()==false{
					for _,in:=range transaction.Vin{
						if in.CanUnlockOutPutWith(address){
							inTXID:=hex.EncodeToString(in.Txid)
							spentTXOs[inTXID]=append(spentTXOs[inTXID],in.Vout)
						}
					}
				}
		}
	}


	return unspentTXs
}

//查找为花费的交易输出的索引和未花费的总数
func (bc *BlockChain)FindSpentableOutputs(address string,amount int)(int,map[string][]int){
	//字典：键是交易的id，值是交易的outputs的索引
	unspentOutputs:=make(map[string][]int)

	accumulated:=0

	unspentTransactions:=bc.FindUnspentTransactions(address)
	Work:
		for _,transaction:=range unspentTransactions{
			txid:=hex.EncodeToString(transaction.ID)

			for outidx,output:=range transaction.Vout{
				if output.CanBeUnlockedWith(address) && accumulated<amount{
					accumulated+=output.Value
					fmt.Println(accumulated)
					unspentOutputs[txid]=append(unspentOutputs[txid],outidx)

					if accumulated>=amount{
						break Work
					}
				}
			}
		}


		return accumulated,unspentOutputs
}

//创建一个带有创世纪区块的数组
func NewBlockChain()*BlockChain{
	var tip []byte
	var genist *Block

	//1.尝试打开或者是创建数据库
	db,err:=bolt.Open(dbFile,0600,nil)
	if err!=nil{
		panic(err)
	}

	err=db.Update(func (tx *bolt.Tx)error{
		//判断表是否存在
		b:=tx.Bucket([]byte(blockBucket))

		if b==nil{ //如果表不存在

			genist=NewGenisBlock(NewCoinBase("lidabao","中本聪"))
			SeriGenist:=genist.SerilizeBlock()

			fmt.Println("这张表不存在，正在创建一张新的表......")

			//创建表
			b,err:=tx.CreateBucket([]byte(blockBucket))
			if err!=nil{
				log.Panic(err)
			}

			//添加数据到表
			err=b.Put(genist.Hash,SeriGenist)
			if err!=nil{
				log.Panic(err)
			}

			//"l"保存的是最后一个区块的hash值
			err=b.Put([]byte("l"),genist.Hash)
			if err!=nil{
				log.Panic(err)
			}

			tip=genist.Hash
		}else{
			tip=b.Get([]byte("l"))
		}

		return nil
	})

	if err!=nil{
		panic(err)
	}

	return &BlockChain{tip,db}
}


//func (bc *BlockChain)AddBlock(data string){
//		newBlock:=NewBlock(data,bc.Tip)
//
//		err:=bc.DB.Update(func (t *bolt.Tx)error{
//
//			b:=t.Bucket([]byte(blockBucket))
//
//			err:=b.Put(newBlock.Hash,newBlock.SerilizeBlock())
//			if err!=nil{
//				panic(err)
//			}
//
//			err=b.Put([]byte("l"),newBlock.Hash)
//			if err!=nil{
//				panic(err)
//			}
//
//			bc.Tip=newBlock.Hash
//
//
//			return nil
//		})
//
//		if err!=nil{
//			panic(err)
//		}
//	}

//挖出一个区块，并且存在数据库中
func (bc *BlockChain)Miner(tx []*Transaction){
	err:=bc.DB.Update(func (t *bolt.Tx)error{
		b:=t.Bucket([]byte(blockBucket))

		newBlock:=NewBlock(tx,bc.Tip)

		bc.Tip=newBlock.Hash

		err:=b.Put(newBlock.Hash,newBlock.SerilizeBlock())
		if err!=nil{
			panic(err)
		}

		err=b.Put([]byte("l"),newBlock.Hash)
		if err!=nil{
			panic(err)
		}

		return nil
	})

	if err!=nil{
		panic(err)
	}
}

//遍历整个区块链
func (bc *BlockChain)ViewBlockChain(){
	err:=bc.DB.View(func (tx *bolt.Tx)error{
		b:=tx.Bucket([]byte(blockBucket))
		theBlock:=b.Get(bc.Tip)
		i:=1
		for  theBlock!=nil{
			block:=UnSerilizeBlock(theBlock)
			fmt.Println("倒数第",i,"个区块的内容是:")
			block.Print()
			i+=1
			theBlock=b.Get(block.PreviewHash)
		}

		return nil
	})
	if err!=nil{
		panic(err)
	}
}